package ua.pp.fland.rest.simple.client;

import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import ua.com.stormlabs.tiny.licenseserver.beans.User;

import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.ResourceBundle;

/**
 * @author mbondarenko
 * @version 1.0 12/26/12
 */
public class LicenseValidator {

    private static final Logger log = LoggerFactory.getLogger(LicenseValidator.class);

    private static final ResourceBundle bundle = ResourceBundle.getBundle("auth");
    private static final String PRIVATE_KEY = bundle.getString("private.key").trim();

    private final String url;

    private final TinyLicenseServerClient tinyLicenseServerClient;

    public LicenseValidator(String url) {
        this.url = url;
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        tinyLicenseServerClient = applicationContext.getBean("licenseClient",
                TinyLicenseServerClient.class);
    }

    public ValidationResult validate(String login, String password) throws SocketException {
        String mac = getMacAddress();
        String opaque = RandomStringUtils.randomAlphanumeric(12);

        String response = tinyLicenseServerClient.getResponse(login, password, mac, opaque, url);

        if (StringUtils.isNotBlank(response) && response.equals(buildActivatedResponse(login, password, opaque, mac))) {
            log.debug("Activated");
            return ValidationResult.USER_ACTIVATED;
        } else if (StringUtils.isNotBlank(response) && response.equals(buildExpectedResponse(login, password, opaque,
                mac))) {
            log.debug("Login OK");
            return ValidationResult.LOGIN_OK;
        } else if (StringUtils.isNotBlank(response) && response.equals(buildAnotherMacActivatedResponse(login, password,
                opaque))) {
            log.debug("Another mac registered to");
            return ValidationResult.USER_WRONG_MAC;
        } else if (StringUtils.isNotBlank(response)) {
            log.debug("Login rejected");
            return ValidationResult.LOGIN_REJECTED;
        } else {
            log.debug("Error while getting response");
            return ValidationResult.WRONG_RESPONSE;
        }
    }

    public User getUserDetails(String login, String password) throws SocketException {
        String mac = getMacAddress();
        String opaque = RandomStringUtils.randomAlphanumeric(12);

        return tinyLicenseServerClient.getUserDetails(login, password, mac, opaque, url);
    }

    private static String buildActivatedResponse(String userName, String licenseCode, String opaque, String mac) {
        String line = userName.trim().toLowerCase() + licenseCode.trim().toLowerCase() + mac.toLowerCase() +
                opaque.toLowerCase() + "{ACTIVATED}" + "{" + PRIVATE_KEY + "}";
        return DigestUtils.md5Hex(line);
    }

    private static String buildAnotherMacActivatedResponse(String userName, String licenseCode, String opaque) {
        return DigestUtils.md5Hex(userName.trim().toLowerCase() + licenseCode.trim().toLowerCase() +
                opaque.trim().toLowerCase() + "{" + PRIVATE_KEY + "}");
    }

    private static String buildExpectedResponse(String userName, String licenseCode, String opaque, String mac) {
        String line = userName.trim().toLowerCase() + licenseCode.trim().toLowerCase() + mac.toLowerCase() +
                opaque.toLowerCase() + "{" + PRIVATE_KEY + "}";
        return DigestUtils.md5Hex(line);
    }

    private static String getMacAddress() throws SocketException {
        log.debug("Getting MAC address");
        String macAddr = null;
        try {
            Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
            while (interfaces.hasMoreElements()) {
                NetworkInterface ni = interfaces.nextElement();
                log.debug("Inspecting network interface " + ni);
                byte[] mac = ni.getHardwareAddress();
                if (mac != null && mac.length > 0) {
                    macAddr = getMacAddr(mac);
                    log.debug("MAC detected: " + Arrays.toString(mac) + "->" + macAddr);
                    break;
                } else {
                    log.debug("MAC is null or empty: " + mac);
                }
            }
        } catch (Exception e) {
            log.error("Failed to get MAC address. Error: " + e, e);
        }
        return macAddr;
    }

    private static String getMacAddr(byte[] addr) throws SocketException {
        return Hex.encodeHexString(addr);
    }
}
